package java_thinking.unit_21._21_7;

import net.jcip.annotations.GuardedBy;

import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * @Desc:   7-22 使用 TrackingExecutor 来保存未完成的任务以备后续执行
 * @author: cww
 * @DateTime: 2019-06-16 17:54
 */

public class WebCrawler {
    private volatile TrackingExecutor exec;
    @GuardedBy("this") private final Set<URL> urlsToCrawl = new HashSet<URL>();
    private final ConcurrentMap<URL, Boolean> seen = new ConcurrentHashMap<URL, Boolean>();
    private static final long TIMEOUT = 500;
    private static final TimeUnit UNIT = MILLISECONDS;

    public WebCrawler(URL startUrl) {
        urlsToCrawl.add(startUrl);
    }
    public synchronized void start() {
        //创建线程池 通过 TrackingExecutor
        exec = new TrackingExecutor(Executors.newCachedThreadPool());
        //遍历添加线程
        for (URL url : urlsToCrawl) {
            submitCrawlTask(url);
        }
        //全部url执行完成后清理
        urlsToCrawl.clear();
    }

    public synchronized void stop() throws InterruptedException {
        try {
            //保存还没执行的线程
            saveUnCrawled(exec.shutdownNow());
            if (exec.awaitTermination(TIMEOUT, UNIT))
                //等待终止超时则将当前被取消的任务线程保存，
                // 这样urlsToCrawl里面就有了未执行的，和执行被取消的
                saveUnCrawled(exec.getCancelledTask());
        } finally {
            exec = null;
        }
    }
    private void saveUnCrawled(List<Runnable> unCrawled) {
        for (Runnable task : unCrawled) {
            urlsToCrawl.add(((CrawlTask) task).getPage());
        }
    }
    private void submitCrawlTask(URL url) {
        exec.execute(new CrawlTask(url));
    }

    private class CrawlTask implements Runnable {
        private final URL url;

        public CrawlTask(URL url) {
            this.url = url;
        }

        /**
         * 检查某个url是否已经执行过了
         * @return
         */
        boolean alreadyCrawled() {
            return seen.putIfAbsent(url, true) !=null;
        }

        void markUncrawled() {
            seen.remove(url);
            System.out.printf("marking %s unCrawled%n", url);
        }
        @Override
        public void run() {
            for (URL url1 : processPage(url)) {
                if (Thread.currentThread().isInterrupted())
                    return;
                submitCrawlTask(url1);
            }
        }
        public URL getPage() {
            return url;
        }

        protected List<URL> processPage(URL url) {
            return null;
        }
    }

}
